package org.orange.feign.simulate.simple.feign.core;

import org.orange.feign.simulate.simple.feign.core.config.ServerUrlTable;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.client.RestTemplate;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.List;
import java.util.Random;

/**
 * @author yuhongtai
 * 代理对象，jdk版
 */
public class SimpleFeignJdkProxy implements InvocationHandler {

    private final String serviceName;

    private final String path;

    private final BeanFactory beanFactory;

    private final String START_WITH = "/";
    
    // private ServerUrlTable serverUrlTable;

    public SimpleFeignJdkProxy(String serviceName, String path, BeanFactory beanFactory) {
        this.serviceName = serviceName;
        this.path = path;
        this.beanFactory = beanFactory;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        PostMapping annotation = AnnotationUtils.findAnnotation(method, PostMapping.class);
        if (annotation == null) {
            return get(proxy, method, args);
        } else {
            return post(proxy, method, args);
        }
    }

    private Object post(Object proxy, Method method, Object[] args) {
        // 1.找到注解
        PostMapping requestMapping = AnnotationUtils.findAnnotation(method, PostMapping.class);
        Assert.notNull(requestMapping, String.format("方法%s不能为空", method.getName()));
        // 2.拼接url，以&开头，表示是SimpleFeign的rpc调用。注意该url并不是一个可以直接调用的url，需要经过自定义的拦截器的处理。
        StringBuilder urlBuilder = initUrlBuilder(requestMapping.value());
        // 3.处理参数
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        Parameter[] parameters = method.getParameters();
        HttpEntity<Object> httpEntity = null;
        for (int i = 0; i < args.length; i++) {
            // 获取方法上的参数
            for (int j = 0; j < parameterAnnotations[i].length; j++) {
                Annotation requestArgAnnotation = parameterAnnotations[i][j];
                Parameter parameter = parameters[i];
                Object arg = args[i];
                // 如果注解是@RequestParam 则拼接到url后面
                if (arg == null) {
                    continue;
                }
                if (requestArgAnnotation instanceof RequestBody && (httpEntity == null)) {
                    httpEntity = new HttpEntity<>(arg);
                } else if (requestArgAnnotation instanceof RequestBody) {
                    throw new IllegalArgumentException("one method can't have two @RequestBody");
                }
            }
        }
        return execute(urlBuilder.toString(), HttpMethod.POST, httpEntity, method.getReturnType());
    }

    private Object get(Object proxy, Method method, Object[] args) {
        // 1.找到注解
        GetMapping requestMapping = AnnotationUtils.findAnnotation(method, GetMapping.class);
        Assert.notNull(requestMapping, String.format("方法%s不能为空", method.getName()));
        // 3.拼接url。注意该url并不是一个可以直接调用的url，需要经过自定义的拦截器的处理。
        StringBuilder urlBuilder = initUrlBuilder(requestMapping.value());
        // 4.处理参数
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        Parameter[] parameters = method.getParameters();
        HttpEntity<Object> httpEntity = null;
        for (int i = 0; i < args.length; i++) {
            // 获取方法上的参数
            for (int j = 0; j < parameterAnnotations[i].length; j++) {
                Annotation requestArgAnnotation = parameterAnnotations[i][j];
                Parameter parameter = parameters[i];
                Object arg = args[i];
                // 如果注解是@RequestParam 则拼接到url后面
                if (arg == null) {
                    continue;
                }
                if (requestArgAnnotation instanceof RequestParam) {

                    if (urlBuilder.indexOf("?") < 0) {
                        urlBuilder.append("?");
                    }
                    urlBuilder.append(parameter.getName())
                            .append("=")
                            .append(arg);
                } else if (requestArgAnnotation instanceof RequestBody && (httpEntity == null)) {
                    httpEntity = new HttpEntity<>(arg);
                } else if (requestArgAnnotation instanceof RequestBody) {
                    throw new IllegalArgumentException("one method can't have two @RequestBody");
                }
            }
        }
        return execute(urlBuilder.toString(), HttpMethod.GET, null, method.getReturnType());
    }

    private StringBuilder initUrlBuilder(String[] uriList){
        Assert.notEmpty(uriList,"uri is Empty!");
        ServerUrlTable table = beanFactory.getBean(ServerUrlTable.class);
        List<String> instanceList = table.get(serviceName);
        String instanceAddress = randomServer(instanceList);
        // TODO
        return new StringBuilder("http://").append(serviceName)
                .append("/").append(path).append("/").append(uriList[0]);
        // return new StringBuilder(instanceAddress)
        //         .append("/")
        //         .append(path).append("/").append(uriList[0]);
    }

    private String randomServer(List<String> instanceList){
        int index = new Random().nextInt(instanceList.size());
        return instanceList.get(index);
    }

    private Object execute(String url, HttpMethod httpMethod, HttpEntity<Object> httpEntity, Class<?> returnType) {
        RestTemplate simpleFeignTemplate = (RestTemplate) beanFactory.getBean("simpleFeignTemplate");
        return simpleFeignTemplate.exchange(url, httpMethod, httpEntity, returnType);
    }
}
